/**********************************************************************

  Audacity: A Digital Audio Editor

  ModuleManager.h

  Dominic Mazzoni
  James Crook

**********************************************************************/

#ifndef __AUDACITY_MODULEMANAGER_H__
#define __AUDACITY_MODULEMANAGER_H__

#include "IteratorX.h"
#include <functional>
#include <map>
#include <memory>
#include <vector>
#include <wx/string.h>

#include "Identifier.h"
#include "PluginProvider.h"

class wxArrayString;
class wxDynamicLibrary;
class ComponentInterface;
class PluginProvider;
class wxWindow;
using PluginID = wxString;
class TranslatableString;

//
// Module Manager
//
// wxPluginManager would be MUCH better, but it's an "undocumented" framework.
//

#include "ModuleConstants.h"

typedef int (* fnModuleDispatch)(ModuleDispatchTypes type);

class Module
{
public:
    Module(const FilePath& name);
    virtual ~Module();

    void ShowLoadFailureError(const wxString& Error);
    bool Load(wxString& deferredErrorMessage);
    void Unload();
    bool HasDispatch() { return mDispatch != NULL; }
    int Dispatch(ModuleDispatchTypes type);
    void* GetSymbol(const wxString& name);
    const FilePath& GetName() const { return mName; }

private:
    const FilePath mName;
    std::unique_ptr<wxDynamicLibrary> mLib;
    fnModuleDispatch mDispatch;
};

class MODULE_MANAGER_API PluginProviderUniqueHandle final
{
    std::unique_ptr<PluginProvider> mPtr;
public:
    PluginProviderUniqueHandle() = default;
    explicit PluginProviderUniqueHandle(std::unique_ptr<PluginProvider> ptr)
        : mPtr(std::move(ptr)) { }
    ~PluginProviderUniqueHandle();

    PluginProviderUniqueHandle(PluginProviderUniqueHandle&&) = default;
    PluginProviderUniqueHandle& operator=(PluginProviderUniqueHandle&&) = default;

    PluginProviderUniqueHandle(const PluginProviderUniqueHandle&) = delete;
    PluginProviderUniqueHandle& operator=(const PluginProviderUniqueHandle&) = delete;

    PluginProvider* get() noexcept { return mPtr.get(); }
    const PluginProvider* get() const noexcept { return mPtr.get(); }

    PluginProvider* operator->() noexcept { return mPtr.get(); }
    const PluginProvider* operator->() const noexcept { return mPtr.get(); }
};

using PluginProviderHandlesMap = std::map<wxString, PluginProviderUniqueHandle>;

class MODULE_MANAGER_API ModuleManager final
{
public:

    // -------------------------------------------------------------------------
    // ModuleManager implementation
    // -------------------------------------------------------------------------

    static ModuleManager& Get();

    // This string persists in configuration files
    // So config compatibility will break if it is changed across Audacity versions
    static wxString GetPluginTypeString();

    static PluginID GetID(const PluginProvider* provider);

private:
    static void FindModules(FilePaths& files);
    using DelayedErrors
        =std::vector< std::pair< std::unique_ptr<Module>, wxString > >;
    static void TryLoadModules(
        const FilePaths& files, FilePaths& decided, DelayedErrors& errors);

public:
    void Initialize();
    int Dispatch(ModuleDispatchTypes type);

    // PluginManager use
    // Can be called before Initialize()
    bool DiscoverProviders();

    // Supports range-for iteration
    auto Providers() const
    { return make_iterator_range(mProviders.cbegin(), mProviders.cend()); }

    auto Providers()
    { return make_iterator_range(mProviders.begin(), mProviders.end()); }

    bool RegisterEffectPlugin(const PluginID& provider, const PluginPath& path, TranslatableString& errMsg);

    PluginProvider* CreateProviderInstance(
        const PluginID& provider, const PluginPath& path);
    std::unique_ptr<ComponentInterface>
    LoadPlugin(const PluginID& provider, const PluginPath& path);

    bool IsProviderValid(const PluginID& provider, const PluginPath& path);
    bool CheckPluginExist(const PluginID& providerId, const PluginPath& path);

private:
    // I'm a singleton class
    ModuleManager();
    ~ModuleManager();
    ModuleManager(const ModuleManager&) = delete;
    ModuleManager& operator=(const ModuleManager&) = delete;

    void InitializeBuiltins();

    friend std::unique_ptr<ModuleManager> std::make_unique<ModuleManager>();

    friend std::default_delete<ModuleManager>;
    static std::unique_ptr<ModuleManager> mInstance;

    // Providers can each report availability of any number of Plug-Ins
    // identified by "paths", and are also factories of ComponentInterface
    // objects for each path
    PluginProviderHandlesMap mProviders;

    // Other libraries that receive notifications of events described by
    // ModuleDispatchTypes:
    std::vector<std::unique_ptr<Module> > mModules;
};

// ----------------------------------------------------------------------------
// A factory of PluginProvider objects
// ----------------------------------------------------------------------------
using PluginProviderFactory = std::unique_ptr<PluginProvider>(*)();

MODULE_MANAGER_API
void RegisterProviderFactory(PluginProviderFactory factory);
MODULE_MANAGER_API
void UnregisterProviderFactory(PluginProviderFactory factory);

// Guarantee the registry exists before any registrations, so it will
// be destroyed only after the un-registrations
static struct Init {
    Init() { RegisterProviderFactory(nullptr); }
} sInitBuiltinModules;

#endif /* __AUDACITY_MODULEMANAGER_H__ */
